﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace NDBGen
{
    public class AggregateStream : Stream
    {
        public AggregateStream(params Stream[] streams)
        {
            this.Streams = streams;
        }

        public Stream[] Streams { get; private set; }

        private ParallelQuery<Stream> ParallelStreams { get { return this.Streams.AsParallel(); } }

        public void Invoke(Action<Stream> action)
        {
            foreach (var stream in this.ParallelStreams)
            {
                action.Invoke(stream);
            }
        }

        public T Invoke<T>(Func<Stream, T> action)
        {
            var results = new List<T>(this.Streams.Length);
            foreach (var stream in this.ParallelStreams)
            {
                results.Add(action.Invoke(stream));
            }

            var firstResult = results.First();
            if (results.All(r => r.Equals(firstResult))) return firstResult;
            else throw new InvalidOperationException("The streams returned different results");
        }

        public override Boolean CanRead
        {
            get { return this.Streams.All(s => s.CanRead); }
        }

        public override Boolean CanSeek
        {
            get { return this.Streams.All(s => s.CanSeek); }
        }

        public override Boolean CanWrite
        {
            get { return this.Streams.All(s => s.CanWrite); }
        }

        public override void Flush()
        {
            this.Invoke(s => s.Flush());
        }

        public override Int64 Length
        {
            get { return this.Invoke(s => s.Length); }
        }

        public override Int64 Position
        {
            get { return this.Invoke(s => s.Position); }
            set { this.Invoke(s => s.Position = value); }
        }

        public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
        {
            if (this.CanRead) return this.Invoke(s => s.Read(buffer, offset, count));
            else throw new NotSupportedException("Read is not supported by all the streams");
        }

        public override Int64 Seek(Int64 offset, SeekOrigin origin)
        {
            if (this.CanSeek) return this.Invoke(s => s.Seek(offset, origin));
            else throw new NotSupportedException("Seek is not supported by all the streams");
        }

        public override void SetLength(Int64 value)
        {
            if (this.CanWrite) this.Invoke(s => s.SetLength(value));
            else throw new NotSupportedException("Write is not supported by all the streams");
        }

        public override void Write(Byte[] buffer, Int32 offset, Int32 count)
        {
            if (this.CanWrite) this.Invoke(s => s.Write(buffer, offset, count));
            else throw new NotSupportedException("Write is not supported by all the streams");
        }
    }
}
